The last few years have been Mexico's most violent in recorded history. 2019 closed as the year with the most homicides since the country started recording these numbers. The figure below shows the increment in the number of homicides in the country since 2015. We can see how most regions have experienced large surges in violence, with the exception of the Northeastern region of the country. The West and Northwest of the country had the sharpest increases in the number of homicides.
#@title
by_region = by_month.groupby(['month', 'region'], as_index=False).sum()
area = alt.Chart(by_region).mark_area().encode(
alt.X('month:T', title='Month'),
alt.Y("homicides:Q", title='Monthly homicides'),
color=alt.Color('region:N', title='Region', legend=alt.Legend(orient='bottom'))
).properties(
width=800,
height=300,
)
line = alt.Chart(by_region).mark_line(size=3).encode(
alt.X('month:T', title='Month'),
alt.Y("homicides:Q", title='Monthly homicides'),
color=alt.Color('region:N', title='Region', legend=None)
).properties(
width=800,
height=300,
)
(area & line).properties(
title={
"text": ['The number of homicides in Mexico has been steadily rising since 2015'],
"subtitle": ['Monthly homicides by region (2015 - 2019)'],
"subtitleFontSize": 14,
}
).resolve_scale(
color='independent'
)
As shown below, the states of Baja California and Chihuahua in the Northwest; and Jalisco, Guanajuato and Michoacán in the West have had the most dramatic increases in the number of homicides. One of the reasons for this has been a series of territorial conflicts between criminal organizations in those parts of the country. In the Central part of the country, both Mexico City and Mexico State (where most of the capital's suburbs are located) also had marked increments in the number of murders commited; while Guerrero, previously the most violent state in the country, has experienced a notable decrease over the last years. While the Southeast of the country is relatively more peaceful than other regions, it too has seen an uptick in the number of murders thanks to spikes in homicides in Oaxaca, Veracruz and Quintana Roo.
#@title
ce, ne, nw, se, we = main_palette
base = alt.Chart(by_month).properties(
width=100,
height=40
)
def state_graph(state, color, ylabel=False, xlabel=False, left=False):
yop = 1
if ylabel:
ytitle = 'Homicides'
yaxis = True
else:
yaxis = False
ytitle = None
if xlabel:
xtitle = 'Month'
xaxis = True
else:
xaxis = False
xtitle = None
if left:
ytitle = 'Homicides'
yop = 0
yaxis = True
return base.transform_filter(
alt.FieldEqualPredicate(field='state', equal=state)
).mark_area(color=color).encode(
alt.X('month:T',
title=xtitle,
axis=alt.Axis(labels=xaxis, ticks=xaxis)),
y=alt.Y('homicides:Q',
title=ytitle,
axis=alt.Axis(labels=yaxis, ticks=yaxis, titleOpacity=yop,
tickOpacity=yop, labelOpacity=yop, labelAngle=-90),
scale=alt.Scale(domain=[0,250])),
facet=alt.Facet('state:N', columns=4, title=None)
)
bc = state_graph('Baja California', nw, ylabel=True)
bcs = state_graph('Baja California Sur', nw, ylabel=True, xlabel=True)
chih = state_graph('Chihuahua', nw, left=True)
son = state_graph('Sonora', nw, left=True)
sin = state_graph('Sinaloa', nw)
dgo = state_graph('Durango', ne, left=True)
coa = state_graph('Coahuila', ne, left=True)
nay = state_graph('Nayarit', we, ylabel=True)
jal = state_graph('Jalisco', we, ylabel=True)
col = state_graph('Colima', we, ylabel=True)
mich = state_graph('Michoacán', we, ylabel=True, xlabel=True)
zac = state_graph('Zacatecas', we, left=True)
ags = state_graph('Aguascalientes', we, left=True)
gto = state_graph('Guanajuato', we, left=True)
qro = state_graph('Querétaro', we)
edomex = state_graph('México', ce, ylabel=True, xlabel=True)
nl = state_graph('Nuevo León', ne, left=True)
slp = state_graph('San Luis Potosí', ne, left=True)
hgo = state_graph('Hidalgo', ce, left=True)
cdmx = state_graph('Ciudad de México', ce, left=True)
mor = state_graph('Morelos', ce)
gro = state_graph('Guerrero', ce, ylabel=True, xlabel=True)
tam = state_graph('Tamaulipas', ne, left=True)
tla = state_graph('Tlaxcala', ce, left=True)
pue = state_graph('Puebla', ce, left=True)
ver = state_graph('Veracruz', se, left=True)
tab = state_graph('Tabasco', se, left=True)
oax = state_graph('Oaxaca', se, xlabel=True, left=True)
cam = state_graph('Campeche', se, left=True)
chia = state_graph('Chiapas', se, xlabel=True, left=True)
yuc = state_graph('Yucatán', se, left=True)
qroo = state_graph('Quintana Roo', se, xlabel=True, left=True)
alt.vconcat(alt.hconcat(bc, son, chih),
alt.hconcat(bcs,
alt.vconcat(alt.hconcat(sin, dgo, coa),
alt.vconcat(alt.hconcat(nay, zac,
alt.hconcat(nl, tam)),
alt.hconcat(jal, ags, slp, pue),
alt.hconcat(col, gto, hgo, tla),
alt.hconcat(mich,
alt.vconcat(alt.hconcat(qro, cdmx, ver),
alt.hconcat(edomex,
alt.vconcat(alt.hconcat(mor, tab, cam, yuc),
alt.hconcat(gro, oax, chia, qroo))))))))
).properties(
title={
"text": ['Baja California, Chihuahua, Guanajuato, Jalisco and Michoacán experienced some of the largest increases in homicides'],
"subtitle": ['Monthly homicides by state (2015 - 2019)'],
"subtitleFontSize": 14,
}
).configure_axisX(
titleFontSize=10,
labelFontSize=10
).configure_axisY(
titleFontSize=10,
labelFontSize=8
)
With the exception of Morelos, Guerrero and Quintana Roo, all of the top 12 states in terms of their homicide rate in 2019 were from the Western or Northwestern part of the country. The Western state of Colima had the highest homicide rate both in 2017 and 2019. Baja California, Chihuahua, Morelos and Guanajuato have experienced dramatic increases in their homicide rates over the last few years. On the other hand Guerrero (once the most violent state in the country), and Baja California Sur (which contains the Los Cabos municipality, the most violent city in the world in 2017) have gone through substantial declines over the past few years.
#@title
by_state = municipalities.groupby(['state', 'region'],as_index=False).sum()
by_state['2015'] = by_state.hom15/(by_state.pop15 / (100000))
by_state['2017'] = by_state.hom17/(by_state.pop17 / (100000))
by_state['2019'] = by_state.hom19/(by_state.pop19 / (100000))
order = list(by_state.sort_values(by='2019', ascending=False).state)
by_state = by_state.melt(id_vars=['state', 'region'], value_vars=['2015', '2017', '2019'], var_name='year', value_name='hom_rate')
def make_bar_chart(df, legend=None):
if legend:
col = alt.Color('region:N', title='Region')
else:
col = alt.Color('region:N', title='Region')
return alt.Chart(df).mark_bar().encode(
y=alt.Y('hom_rate:Q',
scale=alt.Scale(domain=[0,100]),
axis=alt.Axis(title=['Homicides per 100k', 'inhabitants'])),
x=alt.X('year:O', title=None),
color=col,
column= alt.Column('state:N', title=None, sort=order)
).properties(
width=120,
height=100
)
bar1 = make_bar_chart(by_state[by_state.state.isin(order[0:8])], legend=True)
bar2 = make_bar_chart(by_state[by_state.state.isin(order[8:16])])
bar3 = make_bar_chart(by_state[by_state.state.isin(order[16:24])])
bar4 = make_bar_chart(by_state[by_state.state.isin(order[24:32])])
alt.vconcat(bar1, bar2, bar3, bar4
).configure_headerColumn(
labelFontSize=12
).configure_axisY(
titleFontSize=12,
).properties(
title={
"text": ["Colima has highest homicide rate among the states"],
"subtitle": ['Homicide rate by state (2015, 2017 and 2019), sorted by descending order of the 2019 rate'],
"subtitleFontSize": 14,
}
)
The increase in homicides suffered in the West and the Northwest has not been uniformly distributed in these regions. The maps below show the changes in the homicide rate at the municipality level in the 2015-17 and 2017-19 periods. In the Northwest (delimitated in yellow), the increase in rates for the first period was experienced by the majority of the territory, while for the 2017-19 the increase was mainly concentrated in Baja California and certain parts of Chihuahua and Sonora. In this same region, Sinaloa and Baja California Sur are almost entirely blue becasue of the important decreases in violence they experienced the 2017-19 period. In the case of the West (delimitated in black), the increases in violence for the first period seem to be concentrated in Michoacán; Colima, along the coast; and certain parts of Jalisco and Zacatecas. For the 2017-19, a dark red cluster emerged in Guanajuato, Morthern and Western Michoacan and Northern Jalisco. This suggest the increase in violence is a localized phenomenon and has not been generalized across the country.
#@title
states['northwest'] = np.where(states.cve_ent.isin(['02', '03', '08', '26', '25']),
1, 0)
states['west'] = np.where(states.cve_ent.isin(['01', '06', '14', '11', '16',
'18', '22', '32']),
1, 0)
west = states[states.west == 1].dissolve(by='west')
nwest = states[states.northwest == 1].dissolve(by='northwest')
mexico = alt.Chart(municipalities).mark_geoshape(
stroke='gray',
strokeWidth=0.1
).encode(
).properties(
width=500,
height=400
)
westmap = alt.Chart(west).mark_geoshape(
stroke="black",
strokeWidth=2,
fillOpacity=0
).encode(
).properties(
width=500,
height=400
)
nwmap = alt.Chart(nwest).mark_geoshape(
stroke="#ca9a4f",
strokeWidth=2,
fillOpacity=0
).encode(
).properties(
width=500,
height=400
)
change17 = mexico.encode(
fill=alt.Color('quantile17:O',
sort=['< -20.8%', '-20.8% - 0%', '0%', '0% - 31.5%', '> 31.5%'],
title='Percentage change'
)
).properties(
title={
"text": ['2015 - 2017'],
"fontSize": 14
}
)
change19 = mexico.encode(
fill=alt.Color('quantile19:O',
sort=['< -20.8%', '-20.8% - 0%', '0%', '0% - 31.5%', '> 31.5%'],
legend=None
)
).properties(
title={
"text": ['2017 - 2019'],
"fontSize": 14
}
)
alt.hconcat(change17 + westmap + nwmap, change19 + westmap + nwmap).resolve_scale(
fill='independent'
).properties(
title={
"text": ['The fluctuation in homicide rates in the most violent regions has not been uniform'],
"subtitle": ['Percentage change in homicide rates per 100k inhabitants by municipality'],
"subtitleFontSize": 14,
}
)
Most of the big cities in the country experienced increases in their homicide rates between 2015 and 2019. The chart below shows the change in homicides per 100,000 inhabitants by population size. All the cities above the diagonal line experienced increases in their homicide rates in the given period. We can see that most of the large municipalities in the West, Northwest and the Central part of the country experienced upticks in violence. The fact that the bubbles are further away from the diagonal line in the 2015-17 period suggest the rate increases over this period were much worse than in 2017-19.
#@title
# Create labels
muns = list(municipalities.sort_values('pop19',ascending=False)[:40].nomgeo)
municipalities['label'] = np.where(municipalities.nomgeo.isin(muns), municipalities.nomgeo, '')
municipalities.loc[(municipalities.label == 'Juárez') & (municipalities.state != 'Chihuahua'),'label'] = ''
municipalities.loc[(municipalities.label == 'Benito Juárez') & (municipalities.state != 'Ciudad de México') & (municipalities.state != 'Quintana Roo'),'label'] = ''
municipalities.loc[municipalities.label == 'Hermosillo','label'] = ''
municipalities.loc[municipalities.label == 'Naucalpan de Juárez','label'] = ''
municipalities.loc[municipalities.label == 'San Luis Potosí','label'] = ''
municipalities.loc[municipalities.label == 'Acapulco de Juárez','label'] = 'Acapulco'
municipalities.loc[municipalities.label == 'Ecatepec de Morelos','label'] = 'Ecatepec'
municipalities.loc[municipalities.label == 'Gustavo A. Madero','label'] = ''
municipalities.loc[municipalities.label == 'Zapopan','label'] = ''
municipalities.loc[municipalities.label == 'Durango','label'] = ''
municipalities.loc[municipalities.label == 'Apodaca','label'] = ''
municipalities.loc[municipalities.label == 'San Pedro Tlaquepaque','label'] = ''
municipalities.loc[municipalities.label == 'Tlajomulco de Zúñiga','label'] = ''
municipalities.loc[municipalities.label == 'Tlalpan','label'] = ''
municipalities.loc[municipalities.label == 'Álvaro Obregón','label'] = ''
municipalities.loc[municipalities.label == 'Coyoacán','label'] = ''
municipalities.loc[municipalities.label == 'Tlalnepantla de Baz','label'] = ''
municipalities.loc[municipalities.label == 'Chimalhuacán','label'] = ''
municipalities.loc[municipalities.label == 'Torreón','label'] = ''
municipalities.loc[municipalities.label == 'Centro','label'] = ''
base15 = alt.Chart(municipalities).transform_filter(
alt.FieldGTPredicate(field='homrate15', gt=0)
).transform_filter(
alt.FieldGTPredicate(field='homrate17', gt=0)
).transform_filter(
alt.FieldLTPredicate(field='homrate15', lt=256)
).transform_filter(
alt.FieldGTPredicate(field='pop19', gt=100000)
).properties(
width=500,
height=500,
title={
"text": ['2015 - 2017'],
"fontSize": 14
}
)
points15 = base15.mark_circle(size=100).encode(
alt.X('homrate15:Q', scale=alt.Scale(type='log', base=2, domain=[1, 256]), title='Homicides per 100k inhabitants (2015)'),
alt.Y('homrate17:Q', scale=alt.Scale(type='log', base=2, domain=[1, 256]), title='Homicides per 100k inhabitants (2017)'),
color=alt.Color('region:N', title='Region'),
size=alt.Size('pop19:Q', title='Population (2019)'),
)
line15 = base15.mark_line(color='grey').encode(
alt.X('homrate15:Q', scale=alt.Scale(type='log', base=2, domain=[1, 256])),
alt.Y('homrate15:Q', scale=alt.Scale(type='log', base=2, domain=[1, 256]))
)
text15 = base15.mark_text(
align='left',
baseline='middle',
dx=7
).encode(
text='label',
x='homrate15:Q',
y='homrate17:Q'
)
chart15 = line15 + points15 + text15
base17 = alt.Chart(municipalities).transform_filter(
alt.FieldGTPredicate(field='homrate17', gt=0)
).transform_filter(
alt.FieldGTPredicate(field='homrate19', gt=0)
).transform_filter(
alt.FieldLTPredicate(field='homrate17', lt=256)
).transform_filter(
alt.FieldGTPredicate(field='pop19', gt=100000)
).properties(
width=500,
height=500,
title={
"text": ['2017 - 2019'],
"fontSize": 14
}
)
points17 = base17.mark_circle(size=100).encode(
alt.X('homrate17:Q', scale=alt.Scale(type='log', base=2, domain=[1, 256]), title='Homicides per 100k inhabitants (2017)'),
alt.Y('homrate19:Q', scale=alt.Scale(type='log', base=2, domain=[1, 256]), title='Homicides per 100k inhabitants (2019)'),
color=alt.Color('region:N', legend=None),
size=alt.Size('pop19:Q', legend=None),
)
line17 = base17.mark_line(color='grey').encode(
alt.X('homrate17:Q', scale=alt.Scale(type='log', base=2, domain=[1, 256])),
alt.Y('homrate17:Q', scale=alt.Scale(type='log', base=2, domain=[1, 256]))
)
text17 = base17.mark_text(
align='left',
baseline='middle',
dx=7
).encode(
text='label',
x='homrate17:Q',
y='homrate19:Q'
)
chart17 = line17 + points17 + text17
alt.hconcat(chart15, chart17).resolve_scale(
fill='independent'
).properties(
title={
"text": ['Most municipalities with large populations experienced increases in its homicide rates'],
"subtitle": ['Homicide rates per 100,000 inhabitants by population and region', 'Municipalities with more than 100,000 inhabitants in 2019'],
"subtitleFontSize": 14,
}
)
Now we will zoom into Mexico City. As shown before, the City has also experienced an uptick in violence over the previous years. In the graph below, we can see that the most eye-catching increases have been those in Iztapalapa and Gustavo A. Madero municipalities. The rest of the municipalities have remained more or less constant with ups and downs.
#@title
alt.Chart(monthly).mark_area().encode(
alt.X('fecha_hechos:T', title='Month'),
y=alt.Y(
'homicides:Q',
title='Monthly homicides',
),
facet=alt.Facet('alcaldia_hechos:N', columns=4, title=None),
).properties(
width=200,
height=150,
title={
"text": ['Iztapalapa and Gustavo A. Madero municipalities have had an important increase in homicides'],
"subtitle": ['Monthly homicides by municipality, Mexico City (2016 - 2019)'],
"subtitleFontSize": 14,
}
).configure_axisX(
titleFontSize=12
).configure_axisY(
titleFontSize=12
)
Source: Mexico City Open Data Portal
We can see the same phenomenon of concentrated violence as at the national level. We can see several neighborhoods (Iztapalapa, delimitated in yellow) had some of the largest increases. However, most of the municipality did not experience a variation in the number of homicides. In the case of the Gustavo A. Madero municipality (delimited in black), only a handful of neighborhoods were responsible for the increase in violence in the municipality.
#@title
gam = cdmxmap[cdmxmap.cve_mun == '005']
izt = cdmxmap[cdmxmap.cve_mun == '007']
gammap = alt.Chart(gam).mark_geoshape(
stroke="black",
strokeWidth=3,
fillOpacity=0
).encode(
).properties(
width=600,
height=600
)
iztmap = alt.Chart(izt).mark_geoshape(
stroke="#ca9a4f",
strokeWidth=3,
fillOpacity=0
).encode(
).properties(
width=600,
height=600
)
colonias = alt.Chart(colonias).mark_geoshape(
stroke='gray',
strokeWidth=0.1
).encode(
fill=alt.Color('quantile:O',
sort=['< -50%', '-50% - 0%', '0%', '0% - 50%', '> 50%'],
title='Percentage change'
)
).properties(
width=600,
height=600
)
(colonias + iztmap + gammap).properties(
title={
"text": ['A few Southeastern neighborhoods had some of the largest increases in homicides'],
"subtitle": ['Change in homicides by neighborhood, Mexico City (2017-2019)'],
"subtitleFontSize": 14
}
)
Source: Mexico City Open Data Portal